
/*
 *.
 *├── netradio
 *│   ├── channel.h
 *│   ├── netradio.exvim
 *│   ├── radio_types.h
 *│   └── string.h
 *├── tlv_tree.c
 *└── xml_study
 *    ├── a.out
 *    ├── card_info.xml
 *    ├── main.c
 *    └── xml.exvim
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "tlv_tree.h"



static int _parsering_doc(tlv_doc_t root, const char *src, int len);
static void tlv_destory(tlv_doc_t doc);


static tlv_doc_t _create_empty_node(tag_type_t type, const char *name)
{
    tlv_doc_t       doc = NULL;

    doc = calloc(1, sizeof(tlv_node_t));
    if (!doc){
        return doc;
    }
    doc->type = type;
    strcpy(doc->name, name);
    return doc;
}

static tag_type_t THIS_TAG_TYPE(char byte1)
{
    if ((byte1 & 0x20)  == 0x20){
		return TAG_DIR;
	}
	return TAG_FILE;
}

static int tag_name(char *name, const char *src)
{
	//tag df01 2byte
	if ((*src & 0x1f) == 0x1f){
		sprintf(name, "%02X%02X", (unsigned char)src[0], (unsigned char)src[1]);
		return 2;
	}
	//tag 95 1byte
	sprintf(name, "%02X", (unsigned char)src[0]);
	return 1;
	
}

static int value_length(int *len, const char *start)
{
	if ((0x80 & *start) >> 7 != 0){
		start++;
		*len = (unsigned char)*start;
		return 2;
	}
	*len =  (unsigned char)(0x7f & *start);
	return 1;
}

static int tlv_add_one_child(tlv_doc_t father, tlv_node_t *child)
{
	tlv_node_t	*last = NULL;

	if (father->type != TAG_DIR && father->type != TAG_DIR_ROOT){
		return -1;
	}
	if(!father->p.child){
		father->p.child = child;
	}else{
		for (last = father->p.child; last->brother != NULL; last = last->brother){
			;
		}
		last->brother = child;
	}
	child->father = father;
	return 0;
}

static int _func_parser_file(tlv_doc_t root, const char *src, int len)
{
	tlv_doc_t	new_node = NULL;
	const char  *start = src;
	const char	*end = (const char *)(src + len);
	char		name[5] = {0};
	int			length;
    	tlv_value_t *value;

	if (len == 0){
		return 0;
	}
	
	length = tag_name(name, start);
	start += length;
	new_node = _create_empty_node(TAG_FILE, name);
	if (!new_node){
		return -1;
	}

	start += value_length(&length, start);
	new_node->p.value = calloc(1, sizeof(tlv_value_t));
	if (!new_node->p.value){
		free(new_node);
		return -1;
	}
    	value = (tlv_value_t *)new_node->p.value;
	memcpy(value->data, start, length);
	value->len = length;
	tlv_add_one_child(root, new_node);
	start += length;

	return _parsering_doc(root, start, end - start);
}


static int _func_parser_dir(tlv_doc_t root, const char *src, int len)
{
	const char	*start = src;
	const char	*end = (const char *)(src + len);
	tlv_doc_t	new_node = NULL;
	char		name[5] = {0};
    	int         length = 0;

	if (len ==0){
		return 0;
	}
	start += tag_name(name, start);

	new_node = _create_empty_node(TAG_DIR, name);
	if (!new_node){
		return -1;
	}
	tlv_add_one_child(root, new_node);
	start += value_length(&length, start);
	//start += length;
	
	return _parsering_doc(new_node, start, end - start);
}


static int _parsering_doc(tlv_doc_t root, const char *src, int len)
{
	const char	*start = src;
	const char	*end = (const char *)(src + len);
	int		ret;

	if (len <= 0){
		return 0;
	}

	if (THIS_TAG_TYPE(*start) == TAG_DIR){
		ret = _func_parser_dir(root, start, end - start);
	}else if (THIS_TAG_TYPE(*start) == TAG_FILE){
		ret = _func_parser_file(root,start, end - start);
	}
	if (ret != 0){
		return -1;
	}
	return ret;
}




static tlv_doc_t tlv_parser_doc(const char *src, int len)
{
    tlv_doc_t       root = NULL;
    int             ret;

    root = _create_empty_node(TAG_DIR_ROOT, "/");
    if (!root){
        return root;
    }
    ret = _parsering_doc(root, src, len);
    if (ret != 0){
        goto exit_func_err;
    }
    return root;
exit_func_err:
    if (root){
        tlv_destory(root);
    }
    return NULL;
}

static int print_hex(const char *src, int len)
{
	int	i;
	for (i = 0; i < len; ++i)
	{
		printf("%02x", (unsigned char)src[i]);
		/* code */
	}
	printf("\n");
	return 0;
}

static void _tree(tlv_doc_t root, int dep)
{
	int 	i;
	tlv_value_t	*value;

	if (root ==NULL){
		return;
	}
	for (i = 0; i < dep; ++i){
		printf(" ");
	}
	printf("%s\n", root->name);
	if (root->type == TAG_FILE){
		for (i = 0; i < dep + 2; ++i){
			printf(" ");
		}
		value = (tlv_value_t *)root->p.value;
		print_hex(value->data, value->len);

	}
	if(root->type == TAG_DIR || root->type == TAG_DIR_ROOT){
		_tree(root->p.child, dep + 2);
	}
	_tree(root->brother, dep);

	return;
}
static void tree(tlv_doc_t doc)
{
	_tree(doc, 0);
}


static void free_node(tlv_doc_t node)
{
    free(node);
}




static void tlv_destory(tlv_doc_t doc)
{
   if (doc == NULL){
       return ;
   }
    switch(doc->type){
    case TAG_DIR:
    case TAG_DIR_ROOT:
        tlv_destory(doc->p.child);
        break;
    case TAG_FILE:
        free(doc->p.value);
        break;
	case TAG_NONE:
		return;
    }
    tlv_destory(doc->brother);
    free_node(doc);
}


static void tlv_free_tree(tlv_doc_t doc)
{
    if (doc == NULL){
        return ;
    }
    while(doc->father){
        doc = doc->father;
    }
    tlv_destory(doc);
}


static tlv_doc_t tlv_find(tlv_doc_t doc, const char *tag)
{
    if (doc == NULL){
        return NULL;
    }
    if (strcmp(doc->name, tag) == 0){
        return doc;
    }
    if (doc->type == TAG_FILE){
        return tlv_find(doc->brother, tag);
    }
    return tlv_find(doc->p.child, tag);
}


const struct TlvMethod Tlv = {
	tlv_parser_doc,
	tree,
    tlv_free_tree,
    tlv_find
};







